using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections;
using System.Diagnostics;

namespace CompanyX.TechnicalArchitecture.PerfContext
{
    /// <summary>
    /// Provides an encapsulation around the context being instrumented. Based on configuration,
    /// it automatically updates the appropriate counters
    /// </summary>
    internal class PerfContext : IPerfContext
    {
        /// <summary>
        /// Reference to performance counter value
        /// </summary>
        /// <param name="lValue">Returns queried value</param>
        /// <returns>true on success</returns>
        [DllImport("Kernel32.dll", EntryPoint = "QueryPerformanceCounter")]
        protected static extern bool QueryPerformanceCounter(out long lValue);

        protected long startValue = -1;

        protected bool disposed = false;

        #region IPerfContext Members
        private bool ContextInErrorValue = false;
        public bool ContextInError
        {
            get
            {
                return ContextInErrorValue;
            }
            set
            {
                ContextInErrorValue = value;
            }
        }

        private string ContextNameValue = string.Empty;
        public string ContextName
        {
            get { return ContextNameValue; }
        }

        private string InstanceNameValue = string.Empty;
        public string InstanceName
        {
            get { return InstanceNameValue; }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            UpdateCountersOnContextDispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        /// <summary>
        /// Updates contextual counters on creation
        /// </summary>
        private void UpdateCountersOnContextCreation()
        {
            ArrayList counters = PerfContextManager.GetContextCounters(this.ContextName);
            foreach (PerfContextManager.CounterCacheItem counter in counters)
            {
                // Update counter that can be done so on creation of the context
                if ((!counter.IsErrorCounter) &&
                    ((counter.CounterType == PerformanceCounterType.RateOfCountsPerSecond64) ||
                     (counter.CounterType == PerformanceCounterType.NumberOfItems64)))
                    counter.Counter.Increment();

            }
        }

        /// <summary>
        /// Updates contextual counters on creation
        /// </summary>
        /// <param name="disposing">disposing if true; finalizing if false</param>
        private void UpdateCountersOnContextDispose(bool disposing)
        {
            if (!disposed)
            {
                disposed = true;
                ArrayList counters = PerfContextManager.GetContextCounters(this.ContextName);
                if (counters != null)
                {
                    foreach (PerfContextManager.CounterCacheItem counter in counters)
                    {
                        switch (counter.CounterType)
                        {
                            case PerformanceCounterType.NumberOfItems64:
                                {
                                    if (counter.IsErrorCounter)
                                    {
                                        if (this.ContextInError)
                                            counter.Counter.Increment();
                                    }
                                    else
                                        counter.Counter.Increment();
                                }
                                break;

                            case PerformanceCounterType.RateOfCountsPerSecond64:
                            case PerformanceCounterType.AverageBase:
                                counter.Counter.Increment();
                                break;

                            case PerformanceCounterType.AverageTimer32:
                                {
                                    long endValue;
                                    QueryPerformanceCounter(out endValue);
                                    counter.Counter.IncrementBy(endValue - startValue);
                                }
                                break;
                        }
                    }
                    
                }
            }
        }

        /// <summary>
        /// Constructor - provides encapsulation of appropriate counters
        /// </summary>
        /// <param name="contextName">Name of the performance context - must match that of configuration</param>
        public PerfContext(string contextName)
        {
            this.ContextNameValue = contextName;
            QueryPerformanceCounter(out startValue);
        }

        /// <summary>
        /// Constructor - provides encapsulation of appropriate counters
        /// </summary>
        /// <param name="contextName">Name of the performance context - must match that of configuration</param>
        /// <param name="instanceName">Name of the performance context instance - must match that of configuration</param>
        public PerfContext(string contextName, string instanceName)
        {
            this.ContextNameValue = contextName;
            this.InstanceNameValue = instanceName;
            QueryPerformanceCounter(out startValue);
        }

        /// <summary>
        /// Finalizer - should only come into play if this object is not used within an "using" statement
        /// which breaks the pattern and will skew the result
        /// </summary>
        ~PerfContext()
        {
            UpdateCountersOnContextDispose(false);
        }
    }
}
